<?php
//---------------------------------------------------------------------
/**
 * @file			apiCache.api.base.php
 * @CopyRight		(C)1996-2099 SINA Inc.
 * @Project			Waitan
 * @Author			xionghui <xionghui1@staff.sina.com.cn>
 * @Create Date:	2011-08-16
 * @Modified By:	xionghui/2011-08-12
 * @Brief			API 相关的CACHE 处理封装
 */
//---------------------------------------------------------------------
class apiCache_apiBase extends apiBase{
	
	var $prefix = ''; // 缓存前缀
	function __construct() {}
	
	
	/**
	* 批量获取缓存
	* 
	* @param mixed $kname
	* @param mixed $kvars
	* @param int   $isList 　是否 list
	* @return array | false;
	* 			array['values']=array(kvar=>val) 	成功获取的缓存数据
	* 			array['hit_kvars'] 					命中的缓存　kvars
	* 			array['nohit_kvars']				未命中的缓存　kvars
	*/
	function getMuti($kname, $kvars, $isList=false){
		$keys = array();
		$kmap = array();
		$cache = $this->getCache($kname);
		foreach($kvars as $v){
			$kk	= $this->_key($kname, $v);
			$keys[] = $kk;
			$kmap[$kk] = $v;
		}
		$data = $cache->get($keys);
		
		//获取失败
		if ($data===false){return false;}
		if (empty($data)){
			return array('values'=>array(), 'hit_kvars'=>array(), 'nohit_kvars'=>$kvars);
		}
		
		
		$rst = array();
		$rst['values'] = $rst['hit_kvars'] = array();
		foreach ($data as $k=>$v){
			if($v === false || $v === null) {
				continue;
			}
			$rst['values'][$kmap[$k]] = $isList ? ($v ? explode(',', $v) : array()) : $v;
			$rst['hit_kvars'][] = $kmap[$k];
		}
		
		$rst['nohit_kvars'] = array_diff($kvars, $rst['hit_kvars']);
		return $rst;
	}
	
	
	/**
	* 获取一个　API　缓存
	* 
	* @param mixed $kname	缓存标识（KEY　前缀）
	* @param mixed $kvar	缓存关联的变量，通常是 $uid $mid 
	*/
	function get($kname, $kvar) {
		$cache = $this->getCache($kname);
		$key = $this->_key($kname, $kvar);
		return $cache->get($key);
	}
	
	/**
	* 设置一个缓存
	* 
	* @param mixed $kname
	* @param mixed $kvar
	* @param mixed $value
	* @param mixed $ttl
	* @return 失败返回
	*/
	function set($kname, $kvar, $value,$ttl=null){
		$cfg = $this->cfg($kname);
		$cache = $this->getCache($kname);
		$zip = isset($cfg['zip']) ? $cfg['zip']: MEMCACHE_COMPRESSED;		
		$key = $this->_key($kname, $kvar);
		$ttl = $ttl===null ? $cfg['t'] : $ttl;
		$tmp = $cache->set($key, $value, $zip, $ttl);
		
		if (!$tmp){
			vApi::log('API-MC-W-ERROR', '无法写入MC['.$kname.':'.$kvar.']');
		}
		return $tmp;
	}
	
	/**
	* 设置一个缓存
	* 
	* @param mixed $kname
	* @param mixed $kvar
	* @param mixed $value
	* @param mixed $ttl
	* @return 失败返回
	*/
	function add($kname, $kvar, $value,$ttl=null){
		$cfg = $this->cfg($kname);
		$cache = $this->getCache($kname);
		$zip = isset($cfg['zip']) ? $cfg['zip']: MEMCACHE_COMPRESSED;
		$key = $this->_key($kname, $kvar);
		$ttl = $ttl===null ? $cfg['t'] : $ttl;
		$tmp = $cache->add($key, $value, $zip, $ttl);
		
		if (!$tmp){
			vApi::log('API-MC-W-ERROR', '无法写入MC['.$kname.':'.$kvar.']');
		}
		return $tmp;
	}	
	
	/**
	* 增加元素的值　记数器+1操作
	* 
	* @param mixed $kname
	* @param mixed $kvar
	*/
	function inc($kname, $kvar, $value = 1){
		$cache = $this->getCache($kname);
		$key = $this->_key($kname, $kvar);       
		if(($rst = $cache->increment($key, $value)) === false) {
			$setRst = $this->set($kname, $kvar, $value);
            if ($setRst) {
                $rst = $value;
            }
		}
        
        return $rst;
	}
	
	/**
	* 减少元素的值　记数器-1操作
	* 
	* @param mixed $kname
	* @param mixed $kvar
	*/
	function dec($kname, $kvar, $value = 1){
		$cache = $this->getCache($kname);
		$key = $this->_key($kname, $kvar);
		return $cache->decrement($key);
	}
	
	/**
	* 删除一个api缓存
	* 
	* @param mixed $kname
	* @param mixed $kvar
	* @return 失败返回
	*/
	function del($kname, $kvar){
		$cache = $this->getCache($kname);
		$key = $this->_key($kname, $kvar);
		return $cache->delete($key);
	}
	
	
	/**
	* 获取一个列表缓存
	* 
	* @param mixed $kname
	* @param mixed $kvar
	*/
	function listGet($kname, $kvar){
		$listStr = $this->get($kname, $kvar);
		if($listStr===false){return false;}
		return $listStr ? explode(',', $listStr) : array();
	}

	/**
	* 设置一个LIST缓存
	* 
	* @param mixed $kname
	* @param mixed $kvar
	* @param mixed $listArr
	*/
	function listSet($kname, $kvar, $listArr){
		if (!is_array($listArr)){
			vApi::throwException('110009','LIST-SET　方法存储的值必须为数组(内容为IDS,NAMES)');
		}
		$cfg	 = $this->cfg($kname);
		$listStr = implode(',', $listArr);
		return $this->set($kname, $kvar, $listStr, $cfg['t']);
	}
	/**
	* 向某个列表缓存中推进一个节点id
	* 
	* @param mixed $kname	缓存标识（KEY　前缀）
	* @param mixed $kvar	缓存关联的变量，通常是 $uid $mid 
	* @param mixed $node	只能是ID
	* @param mixed $order	ID列表，排序方式 DESC|ASC，默认为 DESC ，降序，新数据 往左插, ASC 往右插
	* @param mixed $isNnique 插入时，是否排重(检查原ID 列表中是否存在此节点，存在刚删除)
	* @return true|flase
	*/
	function listPush($kname, $kvar, $node, $order='DESC', $isNnique=false){
		$cache	= $this->getCache($kname);
		$cfg	= $this->cfg($kname);
		$listStr = $this->get($kname, $kvar);
		if($listStr===false){return false;}

		$listArr = $listStr ? explode(',', $listStr) : array();

		//如果需要排重，则先去重
		if ($isNnique && !empty($listArr) ){
			$listArr = array_diff($listArr, array($node));
		}

		if ($order=='DESC'){
			array_unshift($listArr, $node);
			if (count($listArr)>$cfg['n']){
				array_pop($listArr);
			}
		}else{
			$listArr[]= $node;
			if (count($listArr)>$cfg['n']){
				array_shift($listArr);
			}
		}

		$listStr = implode(',', $listArr);			
		return $this->set($kname, $kvar, $listStr, $cfg['t']);
	}

	/**
	* 从某个列表缓存中删除一个节点ID
	* 
	* @param mixed $kname	缓存标识（KEY　前缀）
	* @param mixed $kvar	缓存关联的变量，通常是 $uid $mid 
	* @param mixed $node	需要删除的节点ID值 可以是数组
	* @return true|flase
	*/
	function listDel($kname, $kvar, $node){
		$cache	= $this->getCache($kname);
		$cfg	= $this->cfg($kname);
		$node	= is_array($node) ? $node : array($node);
		$listStr= $this->get($kname, $kvar);
		if($listStr===false){return false;}

		$listArr = explode(',', $listStr);
		$listArr = array_diff($listArr, $node);
		$listStr = implode(',', $listArr);
		return $this->set($kname, $kvar, $listStr, $cfg['t']);
	}
	
	/*
	 * 缓存清理
	 */
	function flush($mc_num){
		$cache_serv = vApi::cfg('cache_servers');
		$mc = $this->getCache('global');
		return $mc->flush();
	}
	
	/**
	* 获取一个缓存
	* @param mixed $kname	缓存标识
	* @return cache 实例
	*/
	function getCache($kname){
		if(SAE_ACCESSKEY==SAE_ACCESSKEY){
			return vApi::ADP('cache','sae');
		}
		else{
			return $this->getLocalCache($kname);	
		}
	}
	
   /*
	* 获取一个sae环境缓存
	* @param mixed $kname	缓存标识
	* @return cache 实例
	*/
	function getSaeCache($kname){
		static $cacheServers = array();
		$cfg = $this->cfg($kname);

		//根据缓存配置，寻找缓存服务器
		$cache_serv = vApi::cfg('cache_servers', $cfg['mc']);
		if (empty($cache_serv)){
			vApi::throwException('110007', '无法找到缓存服务器['.$cfg['mc'].']');
		}

		$this->prefix = $cache_serv['keyPre'];		
		
		if (isset($cacheServers[$cfg['mc']])){
			return $cacheServers[$cfg['mc']];
		}

		//如果是正式版mc启用跨应用访问,否则使用本应用mc
		if(API_ENABLE_DEBUG == 'sae') {
			$cacheServers[$cfg['mc']] = new Memcached($cfg['mc'], $cache_serv['sae_accesskey']);
		}else{
			$cacheServers[$cfg['mc']] = new Memcached($cfg['mc'], $cache_serv['test_accesskey']);
		}
		
		return $cacheServers[$cfg['mc']];
	}
	
	/**
	* 获取一个本地环境缓存
	* 
	* @param mixed $kname	缓存标识
	* @return cache 实例
	*/	
	function getLocalCache($kname) {
		static $cacheServers = array();
		$cfg = $this->cfg($kname);
		
		//根据缓存配置，寻找缓存服务器
		$cache_serv = vApi::cfg('cache_servers', $cfg['mc']);
		if (empty($cache_serv)){
			vApi::throwException('110007', '无法找到缓存服务器['.$cfg['mc'].']');
		}
		
		$this->prefix = $cache_serv['keyPre'];
		if (isset($cacheServers[$cfg['mc']])){
			return $cacheServers[$cfg['mc']];
		}		
		//-------------------------
		//根据缓存配置获取缓存实例
		$cacheServers[$cfg['mc']] = new Memcache;//Memcached
		$servers = explode(' ', trim($cache_serv['servers']));
		$connect = false;

		foreach ($servers as $server) {
			if (empty($server)) {
				continue;
			}

			$param = explode(':', $server);

            $connect_result = @$cacheServers[$cfg['mc']]->addServer($param[0], $param[1], $cache_serv['pconnect']);
			$connect = $connect || $connect_result;

            /// 记录mc_oapi连接失败日志
            if(false === $connect) {
            	//vApi::throwException('110015', '缓存或者MC链接失败');
                vApi::log('API-MC-C-ERROR', '无法链接MC['.$cfg['mc'].']');
            }
		}
		return $cacheServers[$cfg['mc']];
		//-------------------------
	}
	
	/**
	* 获取一个缓存配置
	* 
	* @param mixed $kname	缓存标识，如 vsl
	* @return 返回相应的配置信息
	*/
	function cfg($kname){
		if (is_array($kname)){
			$kname = $kname['kname'];
		}
		$cache_cfg = vApi::cfg('cache_cfg', $kname);
		if (empty($cache_cfg)){
			vApi::throwException('110006', '无法找到缓存配置['.$kname.']');
		}
		return $cache_cfg;
	}
	
	/**
	* 根据缓存标识获取实际的KEY
	* 
	* @param mixed $kname
	* @param mixed $kvar
	*/
	function _key($kname, $kvar=null){
		if (is_array($kname)){
			$kname = $kname['kname']."_".$kname['kpre'];
		}
		
		$key = $kvar===null ? $kname : $kname."_".$kvar;
		$key = $this->getPrefix().$key;
		return $key;
	}
	
	/*
	 * 获取缓存前缀
	 */
	function getPrefix() {
		return $this->prefix;
	}
	
}
